#
# Copyright 2011 Ugonna Nwakama
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re, logging

from google.appengine.api import channel
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.runtime import DeadlineExceededError
from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError

# User defined libraries
from util import shortuuid
import dsutil, util, entities
from util import render_json_response
from util.constants import DEV_TYPE_NAME_ANDROID as ANDROID
from util.constants import DEV_TYPE_NAME_CHROME as CHROME
from util.constants import RESP_HEADER_KEY_NAME as KEY_NAME
from util.constants import RESP_HEADER_ERROR_MSG as ERROR_MSG

class CheckinRequestHandler(webapp.RequestHandler):
	"""Handles request to check-in by new clients or clients that have lost
their keys."""
	def	get(self):
		"""Handles HEAD requests for checkin by clients. Returns a new key
	embedded in the Response Header."""
		# Get the Android client version (if Android) from the User-Agent
		user_agent = self.request.headers['User-Agent']
		

		android_regex_pattern_1 = 'Android ([0-9]\.[0-9](\.[0-9])?)'
		android_regex_pattern_2 = '([0-9]\.[0-9](\.[0-9])?)'
		android_regex_pattern_3 = '([0-9]\.[0-9]?)'
		chrome_regex_pattern = 'Chrome'

		pattern_match = re.search(android_regex_pattern_1, user_agent)
		
		# String to represent Android version if new device is an Android
		android_version = ''

		if pattern_match:
			dev_type = ANDROID

			# Generate
			android_version = re.search(android_regex_pattern_2, \
				pattern_match.group(0)).group(0)

			# Determine if the Android version is Froyo or higher and,
			# hence, if it supports C2DM.
			allow_push = float(re.search(android_regex_pattern_3, \
				android_version).group(0)) > 2.1 and True
		else:
			pattern_match = re.search(chrome_regex_pattern, user_agent)

			if pattern_match:
				dev_type = CHROME
				allow_push = False
			else:
				# Throw an error to client
				self.error(401) # Unauthorized
				return

		try:
			# Try 10X until a UUID that doesn't exist in the datastore is found
			# xrange has gone away in Py3.x. Plus, the list is too small to 
			# worry about the memory limitations of range. I'm still using
			# xrange, though. Sue me.
			for counter in xrange(0,9):
				pri_key = shortuuid.suuid()

				if not dsutil.check_exists_by_key_name(entities.Device, pri_key):
					break
				if counter == 9:
					logging.error('Could not assign a unique key name to client')
					self.error(503) # Service Unavailable
					return
		except DeadlineExceededError:
			logging.error('Could not assign a unique key name to client. \
			Deadline exceeded')
			
			self.error(503) # Service Unavailable
			return

		device = entities.Device(key_name=pri_key, device_type=dev_type,\
			supports_push=allow_push)
		
		# If an Android version exists, add it
		if android_version:
			device.version = android_version
		
		try:
			device.put()
		except CapabilityDisabledError:
			self.error(503) # Service Unavailable
			return
		else:
			key_data = { 'key' : pri_key }
			render_json_response(self.response, key_data)
			
class PersistConnectionRequestHandler(webapp.RequestHandler):
	"""Handles requests to open a persistent connection for the requesting
client using the Channel API. Validates the client before opening this
connection."""
	def get(self):
		key_name = self.request.get('key_name')
		
		if dsutil.check_exists_by_key_name(entities.Device, key_name):
			token = channel.create_channel(key_name)
			logging.info('Creating channel for %s ' % key_name)
			response_data = {'token': token}
			render_json_response(self.response, response_data)
		else:
			logging.warn('Client attempted to open unauthorized channel using \
			%s ' % key_name)
			self.error(401) # Unauthorized
			response_data = {'error': 'Unrecognized key'}
			render_json_response(self.response, response_data)
			return
	
			
_application = webapp.WSGIApplication([ ('/checkin/channel', PersistConnectionRequestHandler), \
	 ('/checkin', CheckinRequestHandler) ], debug=True)
	
def main():
	logging.getLogger().setLevel(logging.DEBUG)
	run_wsgi_app(_application)
	
if __name__ == '__main__':
	main()
